package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Config 配置结构体
type Config struct {
	FTPHost         string
	FTPPort         int
	FTPUser         string
	FTPPassword     string
	FTPPassive      bool
	LocalAddonPath  string
	LocalAssetsPath string
	RemoteAddonPath string
	RemoteAssetsPath string
	SyncDelay       int
}

// FileWatcher 文件监控器
type FileWatcher struct {
	config    *Config
	ftpClient *FTPClient
	watchers  map[string]bool
	mu        sync.RWMutex
	stopChan  chan bool
}

// loadConfig 加载配置
func loadConfig() (*Config, error) {
	// 尝试加载 .env 文件
	loadEnvFile("env")
	loadEnvFile(".env")
	
	config := &Config{
		FTPHost:         getEnv("FTP_HOST", ""),
		FTPUser:         getEnv("FTP_USER", ""),
		FTPPassword:     getEnv("FTP_PASSWORD", ""),
		FTPPassive:      getEnv("FTP_PASSIVE", "true") != "false",
		LocalAddonPath:  getAbsPath(getEnv("LOCAL_ADDON_PATH", "../gs_demo_thinkphp/addons/gs_demo")),
		LocalAssetsPath: getAbsPath(getEnv("LOCAL_ASSETS_PATH", "../gs_demo_thinkphp/public/assets/addons/gs_demo")),
		RemoteAddonPath: getEnv("REMOTE_ADDON_PATH", "/addons/gs_demo"),
		RemoteAssetsPath: getEnv("REMOTE_ASSETS_PATH", "/public/assets/addons/gs_demo"),
		SyncDelay:       getIntEnv("SYNC_DELAY", 1000),
	}

	if portStr := getEnv("FTP_PORT", "21"); portStr != "" {
		if port, err := strconv.Atoi(portStr); err == nil {
			config.FTPPort = port
		} else {
			config.FTPPort = 21
		}
	} else {
		config.FTPPort = 21
	}

	// 检查必需配置
	if config.FTPHost == "" || config.FTPUser == "" || config.FTPPassword == "" {
		return nil, fmt.Errorf("缺少必要的环境变量: FTP_HOST, FTP_USER, FTP_PASSWORD")
	}

	return config, nil
}

// loadEnvFile 加载 .env 文件
func loadEnvFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		return // 文件不存在，忽略错误
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		
		// 跳过空行和注释
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		
		// 解析 KEY=VALUE 格式
		parts := strings.SplitN(line, "=", 2)
		if len(parts) == 2 {
			key := strings.TrimSpace(parts[0])
			value := strings.TrimSpace(parts[1])
			
			// 如果环境变量未设置，则设置它
			if os.Getenv(key) == "" {
				os.Setenv(key, value)
			}
		}
	}
}

// getEnv 获取环境变量
func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

// getIntEnv 获取整数环境变量
func getIntEnv(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intValue, err := strconv.Atoi(value); err == nil {
			return intValue
		}
	}
	return defaultValue
}

// getAbsPath 获取绝对路径
func getAbsPath(path string) string {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return path
	}
	return absPath
}

// FTPClient 简单的FTP客户端
type FTPClient struct {
	config *Config
	conn   net.Conn
}

// NewFTPClient 创建FTP客户端
func NewFTPClient(config *Config) *FTPClient {
	return &FTPClient{config: config}
}

// Connect 连接FTP服务器
func (c *FTPClient) Connect() error {
	addr := fmt.Sprintf("%s:%d", c.config.FTPHost, c.config.FTPPort)
	fmt.Printf("正在连接到 %s...\n", addr)
	
	conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
	if err != nil {
		return fmt.Errorf("连接失败: %v", err)
	}

	c.conn = conn
	fmt.Println("TCP连接成功，读取服务器响应...")

	// 读取欢迎消息（可能有多行）
	reader := bufio.NewReader(conn)
	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			return fmt.Errorf("读取欢迎消息失败: %v", err)
		}
		fmt.Printf("服务器响应: %s\n", line)
		
		// 检查是否是最后一行（以空格开头表示多行响应的结束，或者以220开头表示单行响应）
		if len(line) > 0 && (line[0] == ' ' || strings.HasPrefix(string(line), "220 ")) {
			break
		}
	}

	// 发送用户名
	_, err = fmt.Fprintf(conn, "USER %s\r\n", c.config.FTPUser)
	if err != nil {
		return fmt.Errorf("发送用户名失败: %v", err)
	}

	response, _, err := reader.ReadLine()
	if err != nil {
		return fmt.Errorf("读取用户名响应失败: %v", err)
	}

	if !strings.HasPrefix(string(response), "331") {
		return fmt.Errorf("用户名认证失败: %s", response)
	}

	// 发送密码
	_, err = fmt.Fprintf(conn, "PASS %s\r\n", c.config.FTPPassword)
	if err != nil {
		return fmt.Errorf("发送密码失败: %v", err)
	}

	response, _, err = reader.ReadLine()
	if err != nil {
		return fmt.Errorf("读取密码响应失败: %v", err)
	}

	if !strings.HasPrefix(string(response), "230") {
		return fmt.Errorf("密码认证失败: %s", response)
	}

	fmt.Println("✓ FTP连接成功")
	return nil
}

// Disconnect 断开连接
func (c *FTPClient) Disconnect() error {
	if c.conn != nil {
		fmt.Fprintf(c.conn, "QUIT\r\n")
		c.conn.Close()
		c.conn = nil
		fmt.Println("FTP连接已断开")
	}
	return nil
}

// TestConnection 测试连接
func (c *FTPClient) TestConnection() error {
	fmt.Println("测试FTP连接...")
	
	if err := c.Connect(); err != nil {
		return err
	}

	// 获取当前目录
	reader := bufio.NewReader(c.conn)
	_, err := fmt.Fprintf(c.conn, "PWD\r\n")
	if err != nil {
		return err
	}

	response, _, err := reader.ReadLine()
	if err != nil {
		return err
	}

	fmt.Printf("✓ 当前目录: %s\n", response)
	return nil
}

// UploadFile 上传文件
func (c *FTPClient) UploadFile(localPath, remotePath string) error {
	// 打开本地文件
	file, err := os.Open(localPath)
	if err != nil {
		return fmt.Errorf("打开本地文件失败: %v", err)
	}
	defer file.Close()

	// 确保远程目录存在
	remoteDir := filepath.Dir(remotePath)
	if err := c.ensureDir(remoteDir); err != nil {
		return err
	}

	reader := bufio.NewReader(c.conn)
	var dataConn net.Conn

	if c.config.FTPPassive {
		// 被动模式
		_, err = fmt.Fprintf(c.conn, "PASV\r\n")
		if err != nil {
			return err
		}

		response, _, err := reader.ReadLine()
		if err != nil {
			return err
		}

		if !strings.HasPrefix(string(response), "227") {
			return fmt.Errorf("被动模式失败: %s", response)
		}

		dataConn, err = c.parsePassiveResponse(string(response))
		if err != nil {
			return fmt.Errorf("解析被动模式响应失败: %v", err)
		}
		defer dataConn.Close()
	} else {
		// 主动模式
		dataConn, err = c.setupActiveMode()
		if err != nil {
			return fmt.Errorf("设置主动模式失败: %v", err)
		}
		defer dataConn.Close()
	}

	// 发送STOR命令
	_, err = fmt.Fprintf(c.conn, "STOR %s\r\n", remotePath)
	if err != nil {
		return err
	}

	response, _, err := reader.ReadLine()
	if err != nil {
		return err
	}

	if !strings.HasPrefix(string(response), "150") {
		return fmt.Errorf("STOR命令失败: %s", response)
	}

	// 传输文件数据
	_, err = file.Seek(0, 0) // 重置文件指针
	if err != nil {
		return err
	}

	_, err = file.WriteTo(dataConn)
	if err != nil {
		return fmt.Errorf("传输文件数据失败: %v", err)
	}

	// 读取传输完成响应
	response, _, err = reader.ReadLine()
	if err != nil {
		return err
	}

	if !strings.HasPrefix(string(response), "226") {
		return fmt.Errorf("文件传输完成响应失败: %s", response)
	}

	fmt.Printf("✓ 上传成功: %s → %s\n", localPath, remotePath)
	return nil
}

// parsePassiveResponse 解析被动模式响应
func (c *FTPClient) parsePassiveResponse(response string) (net.Conn, error) {
	// 解析格式: 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
	start := strings.Index(response, "(")
	end := strings.Index(response, ")")
	if start == -1 || end == -1 {
		return nil, fmt.Errorf("无法解析被动模式响应: %s", response)
	}

	parts := strings.Split(response[start+1:end], ",")
	if len(parts) != 6 {
		return nil, fmt.Errorf("被动模式响应格式错误: %s", response)
	}

	// 解析IP地址
	ip := fmt.Sprintf("%s.%s.%s.%s", parts[0], parts[1], parts[2], parts[3])
	
	// 解析端口
	p1, err := strconv.Atoi(parts[4])
	if err != nil {
		return nil, err
	}
	p2, err := strconv.Atoi(parts[5])
	if err != nil {
		return nil, err
	}
	port := p1*256 + p2

	// 建立数据连接
	addr := fmt.Sprintf("%s:%d", ip, port)
	return net.DialTimeout("tcp", addr, 30*time.Second)
}

// setupActiveMode 设置主动模式
func (c *FTPClient) setupActiveMode() (net.Conn, error) {
	// 监听本地端口
	listener, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	// 获取本地地址和端口
	addr := listener.Addr().(*net.TCPAddr)
	ip := addr.IP
	port := addr.Port

	// 发送PORT命令
	reader := bufio.NewReader(c.conn)
	_, err = fmt.Fprintf(c.conn, "PORT %d,%d,%d,%d,%d,%d\r\n",
		ip[0], ip[1], ip[2], ip[3], port/256, port%256)
	if err != nil {
		listener.Close()
		return nil, err
	}

	response, _, err := reader.ReadLine()
	if err != nil {
		listener.Close()
		return nil, err
	}

	if !strings.HasPrefix(string(response), "200") {
		listener.Close()
		return nil, fmt.Errorf("PORT命令失败: %s", response)
	}

	// 接受连接
	connChan := make(chan net.Conn, 1)
	errChan := make(chan error, 1)

	go func() {
		conn, err := listener.Accept()
		if err != nil {
			errChan <- err
			return
		}
		connChan <- conn
	}()

	// 等待连接或超时
	select {
	case conn := <-connChan:
		listener.Close()
		return conn, nil
	case err := <-errChan:
		listener.Close()
		return nil, err
	case <-time.After(30 * time.Second):
		listener.Close()
		return nil, fmt.Errorf("主动模式连接超时")
	}
}

// ensureDir 确保目录存在
func (c *FTPClient) ensureDir(remotePath string) error {
	// 简化实现，实际应该递归创建目录
	return nil
}

// SyncDirectory 同步目录
func (c *FTPClient) SyncDirectory(localDir, remoteDir string) error {
	fmt.Printf("开始同步目录: %s → %s\n", localDir, remoteDir)
	
	// 检查本地目录是否存在
	if _, err := os.Stat(localDir); os.IsNotExist(err) {
		return fmt.Errorf("本地目录不存在: %s", localDir)
	}

	// 递归同步
	return c.syncDirectoryRecursive(localDir, remoteDir)
}

// syncDirectoryRecursive 递归同步目录
func (c *FTPClient) syncDirectoryRecursive(localDir, remoteDir string) error {
	entries, err := os.ReadDir(localDir)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		// 跳过隐藏文件和目录
		if strings.HasPrefix(entry.Name(), ".") {
			continue
		}

		localPath := filepath.Join(localDir, entry.Name())
		remotePath := remoteDir + "/" + entry.Name()

		if entry.IsDir() {
			// 递归处理子目录
			if err := c.syncDirectoryRecursive(localPath, remotePath); err != nil {
				return err
			}
		} else {
			// 上传文件
			if err := c.UploadFile(localPath, remotePath); err != nil {
				return err
			}
		}
	}

	return nil
}

func main() {
	fmt.Println("🚀 GS Demo FTP同步工具 (简化版)")
	fmt.Println("=" + strings.Repeat("=", 49))

	// 加载配置
	config, err := loadConfig()
	if err != nil {
		fmt.Printf("配置加载失败: %v\n", err)
		fmt.Println("\n请设置以下环境变量:")
		fmt.Println("  FTP_HOST=your-ftp-host")
		fmt.Println("  FTP_USER=your-username")
		fmt.Println("  FTP_PASSWORD=your-password")
		os.Exit(1)
	}

	// 显示配置
	fmt.Println("配置信息:")
	fmt.Printf("  FTP服务器: %s:%d\n", config.FTPHost, config.FTPPort)
	fmt.Printf("  用户名: %s\n", config.FTPUser)
	mode := "被动模式"
	if !config.FTPPassive {
		mode = "主动模式"
	}
	fmt.Printf("  连接模式: %s\n", mode)
	fmt.Printf("  本地插件: %s\n", config.LocalAddonPath)
	fmt.Printf("  远程插件: %s\n", config.RemoteAddonPath)
	fmt.Printf("  本地资源: %s\n", config.LocalAssetsPath)
	fmt.Printf("  远程资源: %s\n", config.RemoteAssetsPath)
	fmt.Println()

	// 创建FTP客户端
	client := NewFTPClient(config)

	// 检查命令行参数
	if len(os.Args) < 2 {
		fmt.Println("用法:")
		fmt.Println("  go run main-simple.go test        - 测试FTP连接")
		fmt.Println("  go run main-simple.go sync        - 同步文件")
		fmt.Println("  go run main-simple.go full-sync   - 完整同步")
		os.Exit(1)
	}

	command := os.Args[1]

	switch command {
	case "test":
		if err := client.TestConnection(); err != nil {
			fmt.Printf("❌ FTP连接测试失败: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("✅ FTP连接测试成功")

	case "sync":
		fmt.Println("执行同步...")
		if err := client.Connect(); err != nil {
			fmt.Printf("❌ FTP连接失败: %v\n", err)
			os.Exit(1)
		}
		defer client.Disconnect()

		// 同步插件目录
		if err := client.SyncDirectory(config.LocalAddonPath, config.RemoteAddonPath); err != nil {
			fmt.Printf("❌ 同步插件目录失败: %v\n", err)
		}

		// 同步资源目录
		if err := client.SyncDirectory(config.LocalAssetsPath, config.RemoteAssetsPath); err != nil {
			fmt.Printf("❌ 同步资源目录失败: %v\n", err)
		}

		fmt.Println("✅ 同步完成")

	case "full-sync":
		fmt.Println("执行完整同步...")
		if err := client.Connect(); err != nil {
			fmt.Printf("❌ FTP连接失败: %v\n", err)
			os.Exit(1)
		}
		defer client.Disconnect()

		// 同步插件目录
		if err := client.SyncDirectory(config.LocalAddonPath, config.RemoteAddonPath); err != nil {
			fmt.Printf("❌ 同步插件目录失败: %v\n", err)
		}

		// 同步资源目录
		if err := client.SyncDirectory(config.LocalAssetsPath, config.RemoteAssetsPath); err != nil {
			fmt.Printf("❌ 同步资源目录失败: %v\n", err)
		}

		fmt.Println("✅ 完整同步完成")

	case "watch":
		fmt.Println("启动文件监控模式...")
		if err := client.Connect(); err != nil {
			fmt.Printf("❌ FTP连接失败: %v\n", err)
			os.Exit(1)
		}
		defer client.Disconnect()

		// 创建文件监控器
		watcher := NewFileWatcher(config, client)
		watcher.Start()

	default:
		fmt.Printf("未知命令: %s\n", command)
		fmt.Println("可用命令: test, sync, full-sync, watch")
		os.Exit(1)
	}
}

// NewFileWatcher 创建文件监控器
func NewFileWatcher(config *Config, ftpClient *FTPClient) *FileWatcher {
	return &FileWatcher{
		config:    config,
		ftpClient: ftpClient,
		watchers:  make(map[string]bool),
		stopChan:  make(chan bool),
	}
}

// Start 启动文件监控
func (fw *FileWatcher) Start() {
	fmt.Println("开始监控文件变化...")
	fmt.Println("监控目录:")
	fmt.Printf("  - %s\n", fw.config.LocalAddonPath)
	fmt.Printf("  - %s\n", fw.config.LocalAssetsPath)
	fmt.Println("按 Ctrl+C 停止监控")

	// 启动监控协程
	go fw.watchDirectory(fw.config.LocalAddonPath, fw.config.RemoteAddonPath, "ThinkPHP插件")
	go fw.watchDirectory(fw.config.LocalAssetsPath, fw.config.RemoteAssetsPath, "前端资源")

	// 等待停止信号
	<-fw.stopChan
	fmt.Println("文件监控已停止")
}

// watchDirectory 监控目录
func (fw *FileWatcher) watchDirectory(localPath, remotePath, displayName string) {
	if _, err := os.Stat(localPath); os.IsNotExist(err) {
		fmt.Printf("警告: %s目录不存在 - %s\n", displayName, localPath)
		return
	}

	fmt.Printf("✓ 开始监控 %s: %s\n", displayName, localPath)

	lastModified := make(map[string]time.Time)

	for {
		select {
		case <-fw.stopChan:
			return
		default:
			fw.scanDirectory(localPath, remotePath, displayName, lastModified)
			time.Sleep(time.Duration(fw.config.SyncDelay) * time.Millisecond)
		}
	}
}

// scanDirectory 扫描目录变化
func (fw *FileWatcher) scanDirectory(localPath, remotePath, displayName string, lastModified map[string]time.Time) {
	err := filepath.Walk(localPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过目录
		if info.IsDir() {
			return nil
		}

		// 检查文件是否被修改
		key := path
		if lastTime, exists := lastModified[key]; exists {
			if info.ModTime().After(lastTime) {
				// 文件被修改，需要上传
				fw.uploadFile(path, localPath, remotePath, displayName)
			}
		} else {
			// 新文件，需要上传
			fw.uploadFile(path, localPath, remotePath, displayName)
		}

		lastModified[key] = info.ModTime()
		return nil
	})

	if err != nil {
		fmt.Printf("扫描目录失败: %v\n", err)
	}
}

// uploadFile 上传文件
func (fw *FileWatcher) uploadFile(filePath, localBasePath, remoteBasePath, displayName string) {
	// 计算相对路径
	relPath, err := filepath.Rel(localBasePath, filePath)
	if err != nil {
		fmt.Printf("计算相对路径失败: %v\n", err)
		return
	}

	// 转换为Unix风格的路径
	relPath = strings.ReplaceAll(relPath, "\\", "/")
	remoteFilePath := remoteBasePath + "/" + relPath

	fmt.Printf("[%s] 检测到文件变化: %s\n", displayName, relPath)

	// 尝试上传文件，如果失败则切换模式重试
	if err := fw.ftpClient.UploadFile(filePath, remoteFilePath); err != nil {
		fmt.Printf("❌ 上传失败: %v\n", err)
		
		// 如果当前是被动模式，尝试主动模式
		if fw.config.FTPPassive {
			fmt.Printf("尝试切换到主动模式重试...\n")
			fw.config.FTPPassive = false
			if err := fw.ftpClient.UploadFile(filePath, remoteFilePath); err != nil {
				fmt.Printf("❌ 主动模式也失败: %v\n", err)
				// 恢复原设置
				fw.config.FTPPassive = true
			} else {
				fmt.Printf("✅ 主动模式上传成功: %s\n", relPath)
			}
		} else {
			// 如果当前是主动模式，尝试被动模式
			fmt.Printf("尝试切换到被动模式重试...\n")
			fw.config.FTPPassive = true
			if err := fw.ftpClient.UploadFile(filePath, remoteFilePath); err != nil {
				fmt.Printf("❌ 被动模式也失败: %v\n", err)
				// 恢复原设置
				fw.config.FTPPassive = false
			} else {
				fmt.Printf("✅ 被动模式上传成功: %s\n", relPath)
			}
		}
	} else {
		fmt.Printf("✅ 上传成功: %s\n", relPath)
	}
}
